﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using Quail;
using QuailDashboard.View.Converters;
using QuailDashboard.View.Data;
using QuailDashboard.View.QuailServiceReference;

namespace QuailDashboard.View.Models
{
    /// <summary>
    /// A test class name for use in the tree-view
    /// </summary>
    public class TestClassHeirarchy : Model, IHierarchalData
    {
        private readonly TestClassHeirarchy _parent;
        private readonly long _key;
        private List<RunStatusResultWrapper> _testlist;
        private int _calculatedStatusId = 1;
        private string _name;
        private static long _keyCounter;

        /// <summary>
        /// Initializes a new instance of the <see cref="TestClassHeirarchy"/> class.
        /// </summary>
        /// <param name="parent">The parent class</param>
        /// <param name="name">The name of the class.</param>
        public TestClassHeirarchy(TestClassHeirarchy parent, string name)
        {
            _key = Interlocked.Increment(ref _keyCounter);
            _parent = parent;
            Name = name;
            ChildList = new TestClassHeirarchyList(this);
        }

        /// <summary>
        /// Delegate for adding test results to a list
        /// </summary>
        /// <param name="rundata">the test result to check</param>
        /// <returns>true if the result should be added</returns>
        public delegate bool AddToResultList(RunStatusResultWrapper rundata);

        /// <summary>
        /// Fires when the Populate async method has completed
        /// </summary>
        public event EventHandler PopulateComplete;

        /// <summary>
        /// Gets the name
        /// </summary>
        public string Name
        {
            get
            {
                return _name;
            }

            private set
            {
                if (_name == null)
                    _name = value;
                else
                {
                    if (_name != value)
                    {
                        _name = value;
                        NotifyPropertyChanged("Name");
                        NotifyPropertyChanged("FullClassName");
                    }
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether the Items are populated
        /// </summary>
        public bool IsPopulated
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Gets the key for the hierarchal data
        /// </summary>
        public long Key
        {
            get
            {
                return _key;
            }
        }

        /// <summary>
        /// Gets the parent of the test class
        /// </summary>
        public IHierarchalData Parent
        {
            get
            {
                return _parent;
            }
        }

        /// <summary>
        /// Gets the status id for the class
        /// </summary>
        public int StatusId
        {
            get
            {
                return _calculatedStatusId;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the test class has tests
        /// </summary>
        public bool HasTests
        {
            get
            {
                foreach (var item in ChildList)
                {
                    if (item.HasTests)
                        return true;
                }

                return _testlist != null;
            }
        }

        /// <summary>
        /// Gets the list of tests for this test class
        /// </summary>
        public IEnumerable<RunStatusResultWrapper> Tests
        {
            get
            {
                return _testlist ?? new List<RunStatusResultWrapper>();
            }
        }
    
       /// <summary>
        /// Gets the full class name
        /// </summary>
        public string FullClassName
        {
            get
            {
                var result = Name;

                if (_parent == null)
                    return result;

                result = _parent.FullClassName + "." + Name;
                if (result.StartsWith("All.")) result = result.Substring(4); // strip off All. prefix

                return result;
            }
        }

        /// <summary>
        /// Gets the list of items in the class
        /// </summary>
        public IList Items
        {
            get
            {
                return ChildList;
            }
        }

        /// <summary>
        /// Gets the list of children test class name's
        /// </summary>
        public TestClassHeirarchyList ChildList
        {
            get;
            private set;
        }

        /// <summary>
        /// Clears the tests
        /// </summary>
        public void ClearTests()
        {
            _testlist = null;

            foreach (var child in ChildList)
            {
                child.ClearTests();
            }
        }

        /// <summary>
        /// Gets the list of test class names at the leaf nodes of the heirarchy
        /// </summary>
        public IEnumerable<string> GetLeafClassNames()
        {
            if (ChildList.Count == 0)
                yield return FullClassName;
            else
            {
                foreach (var child in ChildList)
                {
                    foreach (var fcn in child.GetLeafClassNames())
                        yield return fcn;
                }
            }
        }

        /// <summary>
        /// Gets the list of tests for this test class
        /// </summary>
        public IEnumerable<RunStatusResultWrapper> GetAllTests()
        {
            if (_testlist != null)
            {
                foreach (var t in _testlist)
                    yield return t;
            }

            foreach (var child in ChildList)
            {
                foreach (var t in child.GetAllTests())
                    yield return t;
            }
        }

        /// <summary>
        /// Not used by TestClassName
        /// </summary>
        public void Populate()
        {
            if (PopulateComplete != null)
                PopulateComplete(this, EventArgs.Empty);
        }

        /// <summary>
        /// Adds a test to this test class
        /// </summary>
        /// <param name="test">the test to add</param>
        public void AddTest(RunStatusResultWrapper test)
        {
            if (_testlist == null)
                _testlist = new List<RunStatusResultWrapper>();

            _testlist.Add(test);
            CheckStatusId();

            test.PropertyChanged += OnTestPropertyChanged;
        }

        private void OnTestPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "RunStatusId")
                CheckStatusId();
        }

        private void CheckStatusId()
        {
            var newid = (int) CalculateMyStatusId();

            if (newid != _calculatedStatusId)
            {
                _calculatedStatusId = newid;
                NotifyPropertyChanged("StatusId");
                if (_parent != null)
                    _parent.CheckStatusId();
            }
        }

        private IEnumerable<LogStatus> GetNextStatusId()
        {
            if (_testlist != null)
            {
                foreach (var test in _testlist)
                    yield return (LogStatus) test.RunStatusId;
            }

            foreach (var child in ChildList)
            {
                yield return (LogStatus) child.StatusId;
            }
        }

        private LogStatus CalculateMyStatusId()
        {
            var allkeys = GetNextStatusId()
                .Where(s => s != LogStatus.Pass)
                .GroupBy(s => s)
                .Select(s => new {Status = s.Key, Count = s.Count()})
                .ToList();

            if (allkeys.Any(k => k.Status == LogStatus.Pending))
                return LogStatus.Pending;

            if(allkeys.Any(k => k.Status == LogStatus.AsyncPending))
                return LogStatus.AsyncPending;
            
            var fs = allkeys
                .Where(sc => sc.Status.IsFailingStatus())
                .OrderByDescending(sc => sc.Count)
                .FirstOrDefault();

            if (fs != null)
                return fs.Status;

            fs = allkeys
                .Where(sc => !sc.Status.IsFailingStatus())
                .OrderByDescending(sc => sc.Count)
                .FirstOrDefault();

            if (fs != null)
                return fs.Status;

            return LogStatus.Pass;
        }
    }
}
